package com.bupt.ilink.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.bupt.ilink.entity.FileTranscriptionTaskResponse;
import com.bupt.ilink.entity.Studyspace.CourseSource;
import com.bupt.ilink.mapper.CourseSourceMapper;
import com.bupt.ilink.mapper.FileTranscriptionMapper;
import com.bupt.ilink.service.FileTranscriptionService;
import com.bupt.ilink.service.OssService;
import com.bupt.ilink.service.bailian.FileService;
import com.bupt.ilink.utils.JsonUtils;
import com.bupt.ilink.utils.R;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import com.aliyuncs.CommonRequest;
import com.aliyuncs.CommonResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.FormatType;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.http.ProtocolType;
import com.aliyuncs.profile.DefaultProfile;

import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;

import com.fasterxml.jackson.databind.JsonNode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.mock.web.MockMultipartFile;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;

@Service
public class FileTranscriptionServiceImpl implements FileTranscriptionService {
    @Autowired
    private OssService ossService;

    @Autowired
    private FileTranscriptionMapper fileTransMapper;

    @Autowired
    private CourseSourceMapper courseSourceMapper;

    @Autowired
    private FileService fileService;


    /**
     *
     * @Author Ziyan Ma
     * @Description 创建音视频离线转写任务
     * @Param fileUrl 音视频生成的Url
     * @Param diarizationEnabled 是否开启说话人分离功能
     * @Param speakerCount 如果diarizationEnabled为true,设置说话人分离参数, 0：说话人角色区分结果为不定人数。2：说话人角色区分结果为 2 人。
     * @Param translationEnabled 是否启用翻译功能。
     * @Param language 翻译语言 支持以下取值： cn：中文 en：英文 ja：日文
     * @return json响应
     **/
    @Override
    public FileTranscriptionTaskResponse createFileTransTask(
            String fileUrl,
            boolean diarizationEnabled,
            int speakerCount,
            boolean translationEnabled,
            List<String> language
    ) {
        CommonRequest request = createCommonRequest("tingwu.cn-beijing.aliyuncs.com", "2023-09-30", ProtocolType.HTTPS, MethodType.PUT, "/openapi/tingwu/v2/tasks");
        request.putQueryParameter("type", "offline");

        JSONObject root = new JSONObject();
        // Todo 输入AppKey
        // root.put("AppKey", "vWIWp1cbKmqvCd2g");
        // root.put("AppKey", "vWIWp1cbKmqvCd2g");
        root.put("AppKey", "FXeicg22LxDlzhgJ");

        JSONObject input = new JSONObject();
        input.fluentPut("FileUrl", fileUrl)
                .fluentPut("SourceLanguage", "cn")
                .fluentPut("TaskKey", "task" + System.currentTimeMillis());
        root.put("Input", input);

        JSONObject parameters = initRequestParameters(diarizationEnabled,
                speakerCount,
                translationEnabled,
                language);
        root.put("Parameters", parameters);

        request.setHttpContent(root.toJSONString().getBytes(), "utf-8", FormatType.JSON);

        // Todo 给出AccessKeyId、AccessKeySecret
        //DefaultProfile profile = DefaultProfile.getProfile("cn-beijing", "LTAI5tSaYDzTTqWUmtXMUpnV", "1FVJaz6rBDnBlBvfPjqJhlhBGQxAks");
        DefaultProfile profile = DefaultProfile.getProfile("cn-beijing", "LTAI5tQzna8bSnwnfvFDb2BE", "7snOVVH1F0FLUNaiRgHbHZgDZyzIB7");
        IAcsClient client = new DefaultAcsClient(profile);
        FileTranscriptionTaskResponse taskResponse = new FileTranscriptionTaskResponse();

        try {
            CommonResponse response = client.getCommonResponse(request);
            JSONObject body = JSONObject.parseObject(response.getData());
            taskResponse.setCode(body.getString("Code"));
            taskResponse.setMessage(body.getString("Message"));
            taskResponse.setRequestId(body.getString("RequestId"));
            JSONObject data = body.getJSONObject("Data");
            if (data != null) {
                taskResponse.setTaskId(data.getString("TaskId"));
                taskResponse.setTaskStatus(data.getString("TaskStatus"));
            }
            fileTransMapper.insert(taskResponse);
        } catch (ClientException e) {
            e.printStackTrace();
        }
        return taskResponse;
    }

    /**
     *
     * @Author Ziyan Ma
     * @Description 通过taskId查询当前任务状态以及转换结果，
     *              并将输出文件转存到oss服务器上，
     *              其中路径基于源音视频路径修改
     * @Param taskId 任务Id
     * @Param sourcePath 源音视频文件路径
     * @return FileTranscriptionTaskResponse 响应
     **/
    @Override
    public FileTranscriptionTaskResponse getFileTransInfo (String taskId, String sourcePath) {
        String queryUrl = String.format("/openapi/tingwu/v2/tasks" + "/%s", taskId);

        CommonRequest request = createCommonRequest("tingwu.cn-beijing.aliyuncs.com", "2023-09-30", ProtocolType.HTTPS, MethodType.GET, queryUrl);
        // 设置AccessKeyId、AccessKeySecret
        DefaultProfile profile = DefaultProfile.getProfile("cn-beijing", "LTAI5tQzna8bSnwnfvFDb2BE", "7snOVVH1F0FLUNaiRgHbHZgDZyzIB7");

        // DefaultProfile profile = DefaultProfile.getProfile("cn-beijing", "LTAI5tSaYDzTTqWUmtXMUpnV", "1FVJaz6rBDnBlBvfPjqJhlhBGQxAks");
        IAcsClient client = new DefaultAcsClient(profile);
        FileTranscriptionTaskResponse taskResponse = new FileTranscriptionTaskResponse();
        try {
            CommonResponse response = client.getCommonResponse(request);
            JSONObject body = JSONObject.parseObject(response.getData());
            taskResponse.setCode(body.getString("Code"));
            taskResponse.setMessage(body.getString("Message"));
            taskResponse.setRequestId(body.getString("RequestId"));
            JSONObject data = body.getJSONObject("Data");

            // 提取最后一部分
            int lastSlashIndex = sourcePath.lastIndexOf('/');
            int dotIndex = sourcePath.lastIndexOf('.');
            // System.out.println(SourcePath);
            String rename = sourcePath.substring(lastSlashIndex + 1, dotIndex);
            sourcePath = sourcePath.substring(0, dotIndex) +'/';
            System.out.println(sourcePath);

            if (!sourcePath.endsWith("/")) {
                sourcePath += "/";
            }

            if (data != null) {
                taskResponse.setTaskId(data.getString("TaskId"));
                String taskStatus = data.getString("TaskStatus");
                taskResponse.setTaskStatus(taskStatus);
                System.out.println("TASK基本信息加载\n");
                if (taskStatus.equals("FAILED") || taskStatus.equals("INVALID") ){
                    taskResponse.setErrorCode(data.getString("ErrorCode"));
                    taskResponse.setErrorMessage(data.getString("ErrorMessage"));
                } else{
                    String outputMp3Path = sourcePath + "mp3/audio.mp3";
                    String str = data.getString("OutputMp3Path");
                    System.out.println("MP3 URL:"+ str);
                    if(StringUtils.hasText(str)){
                        ossService.uploadUrl(str, outputMp3Path);
                        String mp3 = "{ \"output_mp3_path\": \"" + outputMp3Path + "\"}";
                        taskResponse.setOutputMp3Path(JsonUtils.toJsonNode(mp3));
                    }

//                    String outputMp4Path = SourcePath + "mp4/video.mp4";
//                    str = data.getString("OutputMp4Path");
//                    System.out.println("MP4 URL:"+ str);
//                    if(StringUtils.hasText(str)){
//                        ossService.uploadUrl(str, outputMp4Path);
//                        String mp4 = "{ \"outputMp4Path\": \"" + outputMp4Path + "\"}";
//                        taskResponse.setOutputMp4Path(JsonUtils.toJsonNode(mp4));
//                    }
                    System.out.println("转码成功\n");
                    JSONObject result = data.getJSONObject("Result");
                    if (result != null ) {
                        str = result.getString("Transcription");
                        if(StringUtils.hasText(str)){
                            JsonNode transcription = fetchJsonFromUrl(str);
                            MultipartFile file = null;
                            if (transcription != null) {
                                file = transJson2File(transcription, rename);
                                R upload = fileService.uploadFile(file, sourcePath, rename+".txt");
                                if(upload.getCode() == 0){
                                    taskResponse.setTaskStatus("FAILED");
                                    taskResponse.setErrorCode(upload.getCode().toString());
                                    taskResponse.setErrorMessage(upload.getMsg());
                                    return taskResponse;
                                }
                            }
                            taskResponse.setTranscription(transcription);

                        }

                        str = result.getString("AutoChapters");
                        if(StringUtils.hasText(str)){
                            JsonNode autoChapters = fetchJsonFromUrl(str);
                            taskResponse.setAutoChapters(autoChapters);
                        }

                        str = result.getString("Summarization");
                        if(StringUtils.hasText(str)){
                            JsonNode summarization = fetchJsonFromUrl(str);
                            taskResponse.setSummarization(summarization);
                        }

                        str = result.getString("MeetingAssistance");
                        if(StringUtils.hasText(str)){
                            JsonNode meetingAssistance = fetchJsonFromUrl(str);
                            taskResponse.setMeetingAssistance(meetingAssistance);
                        }

                        str = result.getString("Translation");
                        if(StringUtils.hasText(str)){
                            JsonNode translation = fetchJsonFromUrl(str);
                            taskResponse.setTranslation(translation);
                        }

                        str = result.getString("PptExtraction");
                        if(StringUtils.hasText(str)){
                            JsonNode pptExtraction = fetchJsonFromUrl(str);
                            pptExtraction = saveFilesfromJson(pptExtraction, sourcePath);
                            taskResponse.setPptExtraction(pptExtraction);
                        }


                        str = result.getString("TextPolish");
                        if(StringUtils.hasText(str)){
                            JsonNode textPolish = fetchJsonFromUrl(str);
                            taskResponse.setTextPolish(textPolish);
                        }
                    }
                }
                fileTransMapper.update(taskResponse, new UpdateWrapper<FileTranscriptionTaskResponse>().eq("task_id", taskResponse.getTaskId()));
            }
        } catch (ClientException e) {
            e.printStackTrace();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return taskResponse;
    }


    /**
     *
     * @Description 将数据表中mp3路径为转移文件后的路径
     * @Param taskId 任务Id
     * @Param tarDir 转移到的目录
     * @return true 修改成功 / false修改失败
     **/
    @Override
    public boolean modifyFileTransInfoPath(
            String taskId,
            String tarDir
    ){
        if (!tarDir.endsWith("/")) {
            tarDir += "/";
        }
        //获取原目录
        CourseSource courseSource = courseSourceMapper.selectOne(new QueryWrapper<CourseSource>().eq("task_id", taskId));
        String sourceId = courseSource.getCourseSourceId().toString() +'/';

        //mp3文件转移
        FileTranscriptionTaskResponse taskResponse = fileTransMapper.selectById(taskId);
        JsonNode oriMp3 = taskResponse.getOutputMp3Path();
        String oriMp3Path = oriMp3.get("output_mp3_path").toString().replace('\"', ' ');
        //mp3后缀获取
        Pattern tarMp3Pattern = Pattern.compile(".*/([^/]+/[^/]+/[^/]+)$");
        Matcher tarMp3Matcher = tarMp3Pattern.matcher(oriMp3Path );
        String tarMp3JsonStr;
        if (tarMp3Matcher.find()) {
            String mp3PathSuffix = tarMp3Matcher.group(1);
            String tarMp3Path = tarDir + mp3PathSuffix;
            tarMp3JsonStr = "{ \"output_mp3_path\": \"" + tarMp3Path + "\"}";
//            System.out.println("filename?mp3 = " + oriMp3Path );
//            System.out.println("转移后路径?mp3 = " +  tarMp3Path);
//            boolean moveMp3 = ossService.moveTranFile(oriMp3Path , tarMp3Path);
//            if (moveMp3){
//                System.out.println("move mp3 Scucess");
//                taskResponse.setOutputMp3Path(JsonUtils.toJsonNode(tarMp3JsonStr) );
//            }
//            else{
//                System.out.println("move mp3 fail.");
//                return false;
//            }
            taskResponse.setOutputMp3Path(JsonUtils.toJsonNode(tarMp3JsonStr) );
        } else {
            System.out.println("No Matched Mp3Path Found.");
            return false;
        }

        JsonNode oriPpt = taskResponse.getPptExtraction();
        if(oriPpt != null){
            JsonNode modifyPpt = replacePath(oriPpt, tarDir, sourceId);
            if(modifyPpt != null){
                taskResponse.setPptExtraction(modifyPpt);
                System.out.println("move ppt file success");
            }else {
                System.out.println("move ppt file fail");
                return false;
            }
        }
        fileTransMapper.updateById(taskResponse);
        return true;
    }

    /**
     *
     * @Description 音视频离线转写任务参数初始化
     * @Param diarizationEnabled 是否开启说话人分离功能
     * @Param speakerCount 如果diarizationEnabled为true,设置说话人分离参数, 0：说话人角色区分结果为不定人数。2：说话人角色区分结果为 2 人。
     * @Param translationEnabled 是否启用翻译功能。
     * @Param language 翻译语言 支持以下取值： cn：中文 en：英文 ja：日文
     * @return JsSONObject 任务参数
     **/
    public static JSONObject initRequestParameters(
            boolean diarizationEnabled,
            int speakerCount,
            boolean translationEnabled,
            List<String> language
    ) {
        JSONObject parameters = new JSONObject();

        // 音视频格式转换
        JSONObject transcoding = new JSONObject();
        // 默认源文件转化为mp3格式
        transcoding.put("TargetAudioFormat", "mp3");
//        transcoding.put("TargetVideoFormat", "mp4");
        transcoding.put("SpectrumEnabled", false);
        parameters.put("Transcoding", transcoding);

        // 语音识别： 可选
        JSONObject transcription = new JSONObject();
        if(diarizationEnabled) {
            transcription.put("DiarizationEnabled", true);
            JSONObject speakerCountParam = new JSONObject();
            speakerCountParam .put("SpeakerCount", speakerCount);
            transcription.put("Diarization", speakerCountParam );
        }
        else{
            transcription.put("DiarizationEnabled", false);
        }
        parameters.put("Transcription", transcription);

        // 翻译： 可选
        JSONObject translation = new JSONObject();
        JSONArray languageArray = new JSONArray();
        if(translationEnabled){
            parameters.put("TranslationEnabled", true);
            languageArray.addAll(language);
            translation.put("TargetLanguages", languageArray);
        }
        else{parameters.put("TranslationEnabled", false);}

        parameters.put("Translation", translation);

        // 章节速览
        parameters.put("AutoChaptersEnabled", true);

        // 智能纪要
        parameters.put("MeetingAssistanceEnabled", true);

        // 摘要相关
        parameters.put("SummarizationEnabled", true);
        JSONObject summarization = new JSONObject();
        JSONArray types = new JSONArray().fluentAdd("Paragraph").fluentAdd("Conversational").fluentAdd("QuestionsAnswering").fluentAdd("MindMap");
        summarization.put("Types", types);
        parameters.put("Summarization", summarization);

        // PPT抽取
        parameters.put("PptExtractionEnabled", true);

        // 口语书面化
        parameters.put("TextPolishEnabled", true);

        return parameters;
    }

    //请求初始化
    public static CommonRequest createCommonRequest(String domain, String version, ProtocolType protocolType, MethodType method, String uri) {
        CommonRequest request = new CommonRequest();
        request.setSysDomain(domain);
        request.setSysVersion(version);
        request.setSysProtocol(protocolType);
        request.setSysMethod(method);
        request.setSysUriPattern(uri);
        request.setHttpContentType(FormatType.JSON);
        return request;
    }

    //提取url链接中数据
    public static JsonNode fetchJsonFromUrl(String urlString) {
        try{
            // 判断非空
            if(urlString.isEmpty()) return null;
            // 创建 URL 对象
            URL url = new URL(urlString);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            connection.setRequestProperty("Accept", "application/json");

            // 读取响应内容
            BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            StringBuilder response = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }
            reader.close();
            // 解析 JSON 数据
            return JsonUtils.toJsonNode(response.toString());

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    //json中非文本数据转存oss
    public JsonNode saveFilesfromJson(JsonNode jsonNode, String sourcePath) {
        if (jsonNode.isNull() ){
            return null;
        }
        // 获取 KeyFrameList
        ArrayNode keyFrameList = (ArrayNode) jsonNode.path("PptExtraction").path("KeyFrameList");

        // 替换 FileUrl
        for (int i = 0; i < keyFrameList.size(); i++) {
            ObjectNode keyFrame = (ObjectNode) keyFrameList.get(i);
            String pptUrl = keyFrame.path("FileUrl").asText();
            String savePath = sourcePath + "ppt/分页/第" + String.valueOf(i+1) + "页.png";
            ossService.uploadUrl(pptUrl, savePath);
            keyFrame.put("FileUrl", savePath);
        }

        // 替换 PdfPath
        String pdfUrl = jsonNode.path("PptExtraction").path("PdfPath").asText();
        String savePath = sourcePath + "ppt/ppt.pdf";
        ossService.uploadUrl(pdfUrl, savePath);
        ((ObjectNode) jsonNode.path("PptExtraction")).put("PdfPath", savePath);

        return jsonNode;
    }

    /**
     *
     * @Author Ziyan Ma
     * @Description 将JsonNode格式的转写结果文本转换成文件，并改写名称
     * @Param transcriptionJson 源json文本
     * @Param rename 改写名称
     * @return MultipartFile 转换后文件
     **/
    public JsonNode replacePath(JsonNode pptJson,  String tarDir, String sourceId) {
        if (pptJson.isNull() ){
            return null;
        }
        // 获取 KeyFrameList
        ArrayNode keyFrameList = (ArrayNode) pptJson.path("PptExtraction").path("KeyFrameList");

        // 替换 pptPath
        boolean modifiedPptImage;
        for (int i = 0; i < keyFrameList.size(); i++) {
            ObjectNode keyFrame = (ObjectNode) keyFrameList.get(i);
            String oriPptPath = keyFrame.path("FileUrl").asText();
            String tarPptPath = tarDir + sourceId + "ppt/分页/" +String.valueOf(i+1) + "页.png";
//            modifiedPptImage = ossService.moveTranFile(oriPptPath, tarPptPath);
//            if(modifiedPptImage){
//                keyFrame.put("FileUrl", tarPptPath);
//            }
//            else{
//                System.out.println("move ppt image fail");
//                return null;
//            }
            keyFrame.put("FileUrl", tarPptPath);
        }

        // 替换 pdfPath
//        String oriPdfPath = pptJson.path("PptExtraction").path("PdfPath").asText();
        String tarPdfPath = tarDir + sourceId + "ppt/ppt.pdf";
//        boolean modifiedPptPdf = ossService.moveTranFile(oriPdfPath, tarPdfPath);
//        if(modifiedPptPdf){
//            ((ObjectNode) pptJson.path("PptExtraction")).put("PdfPath", tarPdfPath);
//        }
//        else {
//            System.out.println("move ppt pdf fail");
//            return null;
//        }
        ((ObjectNode) pptJson.path("PptExtraction")).put("PdfPath", tarPdfPath);
        return pptJson;
    }

    /**
     *
     * @Author Ziyan Ma
     * @Description 将JsonNode格式的转写结果文本转换成文件，并改写名称
     * @Param transcriptionJson 源json文本
     * @Param rename 改写名称
     * @return MultipartFile 转换后文件
     **/
    private MultipartFile transJson2File(JsonNode transcriptionJson, String rename){
        System.out.println("函数调用");
        // 创建 StringBuilder 对象来拼接文本
        StringBuilder paragraphText = new StringBuilder();
        // 获取 Paragraphs 数组
        JsonNode paragraphs = transcriptionJson.path("Transcription").path("Paragraphs");
        // 遍历 Paragraphs 数组
        for (JsonNode paragraph : paragraphs) {
            // 获取 Words 数组
            JsonNode words = paragraph.path("Words");
            // 遍历 Words 数组并拼接文本
            for (JsonNode word : words) {
                String text = word.path("Text").asText();
                paragraphText.append(text);
            }
            // 添加一个换行符以分隔段落
            paragraphText.append("\n");
        }

        // 将拼接的文本转换为字节数组
        byte[] bytes = paragraphText.toString().getBytes(StandardCharsets.UTF_8);
        // 创建 MultipartFile 对象
        MultipartFile multipartFile = null;
//        System.out.println("创建MultipartFile对象");
        try {
            multipartFile = new MockMultipartFile(
                    "file",
                    rename + ".txt",
                    "text/plain",
                    new ByteArrayInputStream(bytes)
            );
        } catch (Exception e) {
            e.printStackTrace();
        }

        return multipartFile;
    }

}
